// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "projfsproviderinterface.h"

#if ZEN_WITH_VFS

#	include <zencore/compactbinarybuilder.h>
#	include <zencore/except.h>
#	include <zencore/string.h>
#	include <zencore/thread.h>

ZEN_THIRD_PARTY_INCLUDES_START
#	if ZEN_PLATFORM_WINDOWS
#		include <zencore/windows.h>
#		include <projectedfslib.h>
#	endif
ZEN_THIRD_PARTY_INCLUDES_END

#	include <unordered_map>
#	include <vector>

namespace zen {

class VfsTree
{
public:
	VfsTree();
	~VfsTree();

	void		 Initialize(VfsTreeDataSource& DataProvider);
	void		 SetTree(VfsTreeNode&& TreeRoot, VfsTreeDataSource& DataProvider);
	VfsTreeNode& GetTree();
	VfsTreeNode* FindNode(std::wstring NodeName);
	void		 ReadChunkData(Oid ChunkId, void* DataBuffer, uint64_t ByteOffset, uint64_t Length);

private:
	struct Impl;

	Impl* m_Impl;
};

//////////////////////////////////////////////////////////////////////////

struct VfsProvider : public ProjFsProviderInterface
{
	struct EnumerationState
	{
		std::string												  Path;
		const VfsTreeNode*										  Node = nullptr;
		std::vector<std::unique_ptr<VfsTreeNode>>::const_iterator Iterator;
		std::vector<std::unique_ptr<VfsTreeNode>>::const_iterator EndIterator;

		void				 RestartScan();
		inline bool			 IsCurrentValid() { return Iterator != EndIterator; }
		bool				 MatchesPattern(PCWSTR SearchExpression);
		void				 MoveToNext();
		const WCHAR*		 GetCurrentFileName();
		PRJ_FILE_BASIC_INFO& GetCurrentBasicInfo();

		PRJ_FILE_BASIC_INFO BasicInfo;
		std::wstring		CurrentName;
	};

	//////////////////////////////////////////////////////////////////////////

	VfsProvider(std::string_view RootPath);
	~VfsProvider();

	void Initialize();
	void Cleanup();

	void AddMount(std::string_view Mountpoint, Ref<VfsTreeDataSource>&& DataSource);
	void RequestStop();

	// ProjFsProviderInterface implementation

	virtual void Run() override;

	virtual HRESULT StartDirEnum(_In_ const PRJ_CALLBACK_DATA* CallbackData, _In_ const GUID* EnumerationId) override;
	virtual HRESULT EndDirEnum(_In_ const PRJ_CALLBACK_DATA* CallbackData, _In_ const GUID* EnumerationId) override;
	virtual HRESULT GetDirEnum(_In_ const PRJ_CALLBACK_DATA*	CallbackData,
							   _In_ const GUID*					EnumerationId,
							   _In_opt_ PCWSTR					SearchExpression,
							   _In_ PRJ_DIR_ENTRY_BUFFER_HANDLE DirEntryBufferHandle) override;
	virtual HRESULT GetPlaceholderInfo(_In_ const PRJ_CALLBACK_DATA* CallbackData) override;
	virtual HRESULT GetFileData(_In_ const PRJ_CALLBACK_DATA* CallbackData, _In_ UINT64 ByteOffset, _In_ UINT32 Length) override;
	virtual HRESULT Notify(_In_ const PRJ_CALLBACK_DATA*		CallbackData,
						   _In_ BOOLEAN							IsDirectory,
						   _In_ PRJ_NOTIFICATION				NotificationType,
						   _In_opt_ PCWSTR						DestinationFileName,
						   _Inout_ PRJ_NOTIFICATION_PARAMETERS* NotificationParameters) override;
	virtual HRESULT QueryFileName(_In_ const PRJ_CALLBACK_DATA* CallbackData) override;
	virtual void	CancelCommand(_In_ const PRJ_CALLBACK_DATA* CallbackData) override;

private:
	std::string m_RootPath;
	Guid		m_RootGuid;
	VfsTree*	m_Tree = nullptr;

	RwLock											m_EnumerationsLock;
	std::unordered_map<zen::Guid, EnumerationState> m_ActiveEnumerations;

	Event			 m_StopRunningEvent;
	std::atomic_flag m_IsRunning;
	std::atomic_flag m_IsInitialized;
};

}  // namespace zen

#endif
